/*******************************************
 * File: utils.cpp
 * Describe: 工具处理
 * Author: Xiangfu DING
 * Time: 2023-12-22
*******************************************/
#include "utils.h"
#include <QDebug>
#include <QProcess>
#include <QStorageInfo>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QFileDialog>
#include <QCoreApplication>
#include <QRandomGenerator>
#include <QStandardPaths>
#include <QTimer>
#include <QTcpSocket>
#include <QApplication>
#include <QSettings>

void Utils::setAutoRun(const QString &appName, const QString &appPath, bool enabled)
{
    QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat);
    if (enabled) {
        QString appPathAdjusted = QDir::toNativeSeparators(appPath);
        appPathAdjusted.replace("/", "\\"); // 将路径中的正斜杠替换为反斜杠
        settings.setValue(appName, appPathAdjusted);
    } else {
        settings.remove(appName);
    }
}

bool Utils::isAutoRunEnabled(const QString &appName, const QString &appPath)
{
    QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat);
    QString appPathAdjusted = QDir::toNativeSeparators(appPath);
    QString value = settings.value(appName).toString();
    return (value == QCoreApplication::applicationFilePath());
}

const QString Utils::generateRandomString(const int &length)
{
    const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
    QString randomString;
    for(int i=0; i<length; ++i) {
        int index = QRandomGenerator::global()->bounded(possibleCharacters.length());
        QChar nextChar = possibleCharacters.at(index);
        randomString.append(nextChar);
    }
    return randomString;
}

const unsigned char *Utils::generateRandomUnsignedChar(unsigned char *randomUnsignedChar, const int &length)
{
    const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
    QByteArray randomData;

    for (int i = 0; i < length; ++i) {
        int index = QRandomGenerator::global()->bounded(possibleCharacters.length());
        QChar nextChar = possibleCharacters.at(index);
        randomData.append(nextChar.toLatin1()); // Convert QChar to unsigned char (Latin-1)
    }

    memcpy(randomUnsignedChar, randomData.constData(), length);
}

const QString Utils::executeCommand(const QString &command, const QStringList &arguments,
                                    const void *inputData, const int dataSize)
{
    QProcess process;
    process.start(command, arguments);
    if (inputData) {
        process.write(QByteArray::fromRawData(reinterpret_cast<const char*>(inputData), dataSize));
        process.closeWriteChannel();
    }
    process.waitForFinished();
    QByteArray output = process.readAll();
    return QString(output);
}

const bool Utils::checkDevicePartitionsExist(const QString &path)
{
    QStringList args;
    args.append("-n");
    args.append("--output");
    args.append("NAME,TYPE");
    args.append(path);
    QString result = executeCommand("lsblk", args);
    QStringList lines = result.split("\n");
    lines.removeAt(0);
    for(const QString& line : lines) {
        if(!line.isEmpty()) {
            QStringList item = line.split(" ");
            if(item.at(1).contains("dm")) {
                continue;
            }
            if(item.at(0).right(1).at(0).isDigit()) {
                return true;
            }
        }
    }
    return false;
}

const bool Utils::checkFileOccupancy(const QString &path)
{
    QStringList args;
    args.append("-m");
    args.append(path);
    QString result = executeCommand("fuser", args);
    if(!result.isEmpty()) return true;

    return false;
}

const int Utils::getFileSize(const QString &path)
{
    QFileInfo fileInfo(path);
    return fileInfo.size();
}

const QVariantMap Utils::getFilesystemInfo(const QString &path)
{
    QJsonObject jsonObject;

    QStringList units = {"Bytes", "KB", "MB", "GB", "TB"};
    int unitIndex = 0;

    QStorageInfo storage(path);
    jsonObject.insert("Device", QString::fromUtf8(storage.device()));
    jsonObject.insert("Type", QString::fromUtf8(storage.fileSystemType()));
    double totalSize = storage.bytesTotal();
    while (totalSize > 1024 && unitIndex < units.size()) { totalSize /= 1024; unitIndex++; }
    jsonObject.insert("TotalSize", QString::number(totalSize, 'f', 2) + " " + units[unitIndex]);
    unitIndex = 0;
    double availableSize = storage.bytesAvailable();
    while (availableSize > 1024 && unitIndex < units.size()) { availableSize /= 1024; unitIndex++; }
    jsonObject.insert("AvailableSize", QString::number(availableSize, 'f', 2) + " " + units[unitIndex]);
    double usePercent = ((totalSize-availableSize)/totalSize)*100;
    jsonObject.insert("Percent", usePercent);

    QJsonDocument jsonDocument(jsonObject);
    QByteArray jsonArrayData = jsonDocument.toJson(QJsonDocument::Compact);
    QJsonDocument document = QJsonDocument::fromJson(jsonArrayData);
    return document.toVariant().toMap();
}

const QString Utils::getDeviceMountpoint(const QString &path)
{
    QString mountPoint;
    QList<QStorageInfo> storageInfoList = QStorageInfo::mountedVolumes();
    for (const QStorageInfo &storageInfo : storageInfoList) {
        if (storageInfo.device() == path) {
            mountPoint = storageInfo.rootPath();
            break;
        }
    }
    if (!mountPoint.isEmpty()) {
        return mountPoint;
    } else {
        return "";
    }
}

void Utils::remountRoFilesystem(const QString &path)
{
    QStringList args;
    args.append("-o");
    args.append("remount,ro");
    args.append(path);
    Utils::executeCommand("mount", args);
}

void Utils::remountRWFilesystem(const QString &path)
{
    QStringList args;
    args.append("-o");
    args.append("remount,rw");
    args.append(path);
    Utils::executeCommand("mount", args);
}

const bool Utils::umountDeviceFilesystem(const QString &path)
{
    QStringList args;
    args.append("-n");
    args.append("--output");
    args.append("MOUNTPOINT");
    args.append(path);
    QString mountpoint = Utils::executeCommand("lsblk", args);
    mountpoint.replace("\n", "");
    if(!mountpoint.isEmpty()) {
        if(!checkFileOccupancy(mountpoint)) {
            args.clear();
            args.append("--");
            args.append(mountpoint);
            Utils::executeCommand("umount", args);
            return true;
        } else {
            return false;
        }
    } else {
        return true;
    }
}

const bool Utils::openFileBrowser(const QString &path)
{
    QString command = "xdg-open "+path;
    QProcess process;
    process.startDetached("/bin/bash", QStringList() << "-c" << command);
    process.waitForFinished();

    return true;
}

const bool Utils::isAutoStart()
{
    QString directoryPath = "/etc/xdg/autostart";
    QString fileName = "AuditCrypt.desktop";
    QDir dir(directoryPath);
    return dir.exists(fileName);
}

const bool Utils::enableAutoStart(bool enable)
{
    QString fileName = "AuditCrypt.desktop";
    QString destinationFilePath = "/etc/xdg/autostart";
    QString sourceFilePath = QApplication::applicationDirPath()+"/"+"AuditCrypt.desktop";

    if(enable) {
        QStringList argv;
        argv << sourceFilePath << destinationFilePath;
        executeCommand("cp", argv);
        return isAutoStart();
    } else {
        QDir dir(destinationFilePath);
        if(dir.exists(fileName)) {
            return QFile::remove(destinationFilePath+"/"+fileName);
        }
        return false;
    }
}
